#include "HTTPClient.h"
#include "esp_log.h"
#include "esp_crt_bundle.h" // For HTTPS
#include <string>          // For std::to_string
#include <sstream>         // For std::ostringstream
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <inttypes.h> // For PRId64

static const char *TAG = "HTTPClient";

// Structure to pass parameters to the asynchronous task
struct AsyncTaskParams {
    HTTPClient* client_instance;
    std::string full_url;
    esp_http_client_method_t method;
    std::string body;
    std::string content_type;
    std::map<std::string, std::string> custom_headers; // Combined with default headers by the task
    HttpCallback original_callback;
};

// --- Static HTTP Event Handler ---
esp_err_t HTTPClient::http_event_handler(esp_http_client_event_t *evt) {
    HTTPClient* client_instance = static_cast<HTTPClient*>(evt->user_data);

    if (!client_instance && evt->event_id != HTTP_EVENT_ERROR) {
        ESP_LOGE(TAG, "HTTP event handler: null client_instance for event_id: %d", evt->event_id);
        return ESP_FAIL;
    }

    switch(evt->event_id) {
        case HTTP_EVENT_ERROR:
            ESP_LOGE(TAG, "HTTP_EVENT_ERROR");
            // Detailed error logging can be added here if needed by the instance
            break;
        case HTTP_EVENT_ON_CONNECTED:
            ESP_LOGI(TAG, "HTTP连接建立");
            break;
        case HTTP_EVENT_HEADER_SENT:
            break;
        case HTTP_EVENT_ON_HEADER:
            if (client_instance && evt->header_key && evt->header_value) {
                client_instance->response_headers_[evt->header_key] = evt->header_value;
            }
            break;
        case HTTP_EVENT_ON_DATA:
            if (client_instance) {
                size_t old_size = client_instance->response_data_buffer_.size();
                client_instance->response_data_buffer_.insert(
                    client_instance->response_data_buffer_.end(),
                    (char*)evt->data,
                    (char*)evt->data + evt->data_len
                );
            } else {
                 ESP_LOGE(TAG, "HTTP_EVENT_ON_DATA: client_instance is null!");
            }
            break;
        case HTTP_EVENT_ON_FINISH:
            ESP_LOGI(TAG, "HTTP请求完成");
            break;
        case HTTP_EVENT_DISCONNECTED:
            ESP_LOGI(TAG, "HTTP连接断开");
            break;
        case HTTP_EVENT_REDIRECT:
            ESP_LOGI(TAG, "HTTP重定向到: %s", (char*)evt->data);
            break;
        default:
            break;
    }
    return ESP_OK;
}

// --- Constructor & Destructor ---
HTTPClient::HTTPClient() {
    // base_url_, default_headers_ are inherited and default-constructed
    // timeout_ms_ is inherited and initialized (e.g. 10000 in HttpInterface)
    response_data_buffer_.reserve(2048); // Pre-allocate
}

HTTPClient::~HTTPClient() {
    // No specific cleanup needed for client_handle_ as it's managed per-request
}

// --- Helper Methods ---
std::string HTTPClient::getString() {
    if (!response_data_buffer_.empty()) {
        return std::string(response_data_buffer_.begin(), response_data_buffer_.end());
    }
    return "";
}

const std::map<std::string, std::string>& HTTPClient::getResponseHeaders() const {
    return response_headers_;
}

std::string HTTPClient::build_full_url(const std::string& path, const std::map<std::string, std::string>& query_params) {
    std::string full_url;
    if (path.rfind("http://", 0) == 0 || path.rfind("https://", 0) == 0) {
        full_url = path; // Path is already a full URL
    } else {
        full_url = base_url_;
        if (!base_url_.empty() && base_url_.back() == '/' && !path.empty() && path.front() == '/') {
            full_url.pop_back(); // Avoid double slash if base ends with / and path starts with /
        } else if (!base_url_.empty() && base_url_.back() != '/' && !path.empty() && path.front() != '/') {
             full_url += "/"; // Add slash if missing between base and path
        }
        full_url += path;
    }

    if (!query_params.empty()) {
        std::ostringstream query_stream;
        bool first_param = true;
        if (full_url.find('?') == std::string::npos) {
            query_stream << "?";
        } else {
            query_stream << "&"; // Append to existing query string
        }
        
        for (const auto& pair : query_params) {
            if (!first_param) {
                query_stream << "&";
            }
            // Simple URL encoding for key and value can be added here if necessary
            // For now, assuming they are already safe or don't need encoding.
            // esp_http_client_set_query_params might be an option if the IDF version supports it well.
            query_stream << pair.first << "=" << pair.second;
            first_param = false;
        }
        full_url += query_stream.str();
    }
    return full_url;
}

// --- Configuration Method Implementations ---
void HTTPClient::setBaseUrl(const std::string& base_url) {
    base_url_ = base_url;
    // Ensure base_url_ ends with a slash if it's not empty and doesn't have one,
    // or handle this logic in build_full_url. For now, build_full_url handles it.
}

void HTTPClient::setDefaultHeader(const std::string& name, const std::string& value) {
    default_headers_[name] = value;
}

void HTTPClient::removeDefaultHeader(const std::string& name) {
    default_headers_.erase(name);
}

void HTTPClient::clearDefaultHeaders() {
    default_headers_.clear();
}

void HTTPClient::setTimeout(int timeout_ms) {
    timeout_ms_ = timeout_ms;
}


// --- Synchronous Method Core Logic ---
HttpResponse HTTPClient::perform_sync_request(
    const std::string& path,
    esp_http_client_method_t method,
    const std::map<std::string, std::string>& query_params,
    const std::string& body,
    const std::string& content_type,
    const std::map<std::string, std::string>& custom_headers) {

    std::string full_url_str = build_full_url(path, query_params);
    ESP_LOGI(TAG, "开始HTTP请求: %s", full_url_str.c_str());

    response_data_buffer_.clear();
    response_headers_.clear();

    esp_http_client_config_t config = {};
    config.url = full_url_str.c_str();
    config.event_handler = http_event_handler;
    config.user_data = this;
    config.timeout_ms = this->timeout_ms_;
    if (full_url_str.rfind("https://", 0) == 0) {
        config.crt_bundle_attach = esp_crt_bundle_attach;
    }

    esp_http_client_handle_t client_handle = esp_http_client_init(&config);
    if (!client_handle) {
        ESP_LOGE(TAG, "HTTP客户端初始化失败");
        // Corrected HttpResponse construction
        return HttpResponse(0, {}, "", "Failed to initialize HTTP client for sync request.");
    }

    esp_http_client_set_method(client_handle, method);

    for (const auto& header : default_headers_) {
        esp_http_client_set_header(client_handle, header.first.c_str(), header.second.c_str());
    }
    for (const auto& header : custom_headers) {
        esp_http_client_set_header(client_handle, header.first.c_str(), header.second.c_str());
    }

    if (method == HTTP_METHOD_POST || method == HTTP_METHOD_PUT || method == HTTP_METHOD_PATCH) {
        if (!content_type.empty()) {
            esp_http_client_set_header(client_handle, "Content-Type", content_type.c_str());
        } else if (default_headers_.count("Content-Type")) {
            // Use default if not specified per request
        } else if (!body.empty()){
             // Default to application/octet-stream or application/json if body looks like json
             // For simplicity, let user set it or rely on server.
             // Or, could default to "application/x-www-form-urlencoded" or "text/plain"
        }
        esp_http_client_set_post_field(client_handle, body.c_str(), body.length());
    }
    
    esp_err_t err = esp_http_client_perform(client_handle);

    HttpResponse response;
    response.errorMessage = ""; // Clear any previous error

    if (err == ESP_OK) {
        response.statusCode = esp_http_client_get_status_code(client_handle);
        response.success = (response.statusCode >= 200 && response.statusCode < 300);
        if (!response.success) {
            ESP_LOGW(TAG, "HTTP请求状态码: %d", response.statusCode);
        } else {
            ESP_LOGI(TAG, "HTTP请求成功: %d", response.statusCode);
        }
    } else {
        ESP_LOGE(TAG, "HTTP请求失败: %s", esp_err_to_name(err));
        response.statusCode = 0; // Or a specific error code like -1
        response.success = false;
        response.errorMessage = std::string("HTTP request failed: ") + esp_err_to_name(err);
    }

    response.headers = this->response_headers_; // Headers collected by event handler
    response.body = this->getString();       // Body collected by event handler

    esp_http_client_cleanup(client_handle);
    return response;
}

// --- Synchronous Method Implementations ---
HttpResponse HTTPClient::getSync(const std::string& path, const std::map<std::string, std::string>& query_params, const std::map<std::string, std::string>& custom_headers) {
    return perform_sync_request(path, HTTP_METHOD_GET, query_params, "", "", custom_headers);
}

HttpResponse HTTPClient::postSync(const std::string& path, const std::string& body, const std::string& content_type, const std::map<std::string, std::string>& custom_headers) {
    return perform_sync_request(path, HTTP_METHOD_POST, {}, body, content_type, custom_headers);
}

HttpResponse HTTPClient::putSync(const std::string& path, const std::string& body, const std::string& content_type, const std::map<std::string, std::string>& custom_headers) {
    return perform_sync_request(path, HTTP_METHOD_PUT, {}, body, content_type, custom_headers);
}

HttpResponse HTTPClient::delSync(const std::string& path, const std::map<std::string, std::string>& custom_headers) {
    return perform_sync_request(path, HTTP_METHOD_DELETE, {}, "", "", custom_headers);
}

HttpResponse HTTPClient::patchSync(const std::string& path, const std::string& body, const std::string& content_type, const std::map<std::string, std::string>& custom_headers) {
    return perform_sync_request(path, HTTP_METHOD_PATCH, {}, body, content_type, custom_headers);
}


// --- Asynchronous Task Function ---
void HTTPClient::http_async_task_function(void* pvParameters) {
    AsyncTaskParams* params = static_cast<AsyncTaskParams*>(pvParameters);
    HTTPClient* self = params->client_instance; // 'self' is the HTTPClient instance

    ESP_LOGI(TAG, "启动异步HTTP任务: %s", params->full_url.c_str());

    // Clear instance buffers for this request.
    // This assumes one async operation at a time on this instance, or careful management.
    // If multiple async ops can be truly concurrent on one instance, buffers need to be per-call.
    // For now, let's assume the instance's buffers are dedicated to the current async op handled by this task.
    self->response_data_buffer_.clear();
    self->response_headers_.clear();

    esp_http_client_config_t config = {};
    config.url = params->full_url.c_str();
    config.event_handler = http_event_handler; // This will use 'self' for buffers
    config.user_data = self; 
    config.timeout_ms = self->timeout_ms_;
    if (params->full_url.rfind("https://", 0) == 0) {
        config.crt_bundle_attach = esp_crt_bundle_attach;
    }

    esp_http_client_handle_t client_handle = esp_http_client_init(&config);
    HttpResponse response; // Prepare response object

    if (!client_handle) {
        ESP_LOGE(TAG, "异步HTTP客户端初始化失败");
        // Correctly initialize response before callback
        response.statusCode = 0;
        response.headers = {};
        response.body = "";
        response.errorMessage = "Failed to initialize HTTP client for async request.";
        // response.success will be false by default or due to statusCode 0 / errorMessage
        if (params->original_callback) {
            params->original_callback(response);
        }
        delete params; // Clean up params
        vTaskDelete(NULL); // Delete self
        return;
    }

    esp_http_client_set_method(client_handle, params->method);

    // Apply headers (default from 'self', then custom from 'params')
    for (const auto& header : self->default_headers_) {
        esp_http_client_set_header(client_handle, header.first.c_str(), header.second.c_str());
    }
    for (const auto& header : params->custom_headers) { // Custom headers from the specific async call
        esp_http_client_set_header(client_handle, header.first.c_str(), header.second.c_str());
    }
    
    if (params->method == HTTP_METHOD_POST || params->method == HTTP_METHOD_PUT || params->method == HTTP_METHOD_PATCH) {
        if (!params->content_type.empty()) {
            esp_http_client_set_header(client_handle, "Content-Type", params->content_type.c_str());
        } else if (self->default_headers_.count("Content-Type")) {
            // Use default if not specified per request
        } else if (!params->body.empty()){
            // Defaulting logic as in sync
        }
        esp_http_client_set_post_field(client_handle, params->body.c_str(), params->body.length());
    }

    esp_err_t err = esp_http_client_perform(client_handle);

    if (err == ESP_OK) {
        response.statusCode = esp_http_client_get_status_code(client_handle);
        response.success = (response.statusCode >= 200 && response.statusCode < 300);
        if (!response.success) {
            ESP_LOGW(TAG, "异步HTTP请求状态码: %d", response.statusCode);
        } else {
            ESP_LOGI(TAG, "HTTP请求成功: %d", response.statusCode);
        }
    } else {
        ESP_LOGE(TAG, "异步HTTP请求失败: %s", esp_err_to_name(err));
        response.statusCode = 0;
        response.success = false;
        response.errorMessage = std::string("HTTP async request failed: ") + esp_err_to_name(err);
    }

    response.headers = self->response_headers_; // Collected by event_handler via 'self'
    response.body = self->getString();       // Collected by event_handler via 'self'

    esp_http_client_cleanup(client_handle);

    if (params->original_callback) {
        params->original_callback(response);
    }

    delete params; // Clean up dynamically allocated params
    vTaskDelete(NULL); // Task deletes itself
}

// --- Asynchronous Method Implementations ---
void HTTPClient::get(const std::string& path, HttpCallback callback, const std::map<std::string, std::string>& query_params, const std::map<std::string, std::string>& custom_headers) {
    AsyncTaskParams* params = new AsyncTaskParams();
    params->client_instance = this;
    params->full_url = build_full_url(path, query_params);
    params->method = HTTP_METHOD_GET;
    // params->body, params->content_type are empty for GET
    params->custom_headers = custom_headers;
    params->original_callback = callback;

    xTaskCreate(http_async_task_function, "http_get_task", 8192, params, 5, NULL);
}

void HTTPClient::post(const std::string& path, const std::string& body, HttpCallback callback, const std::string& content_type, const std::map<std::string, std::string>& custom_headers) {
    AsyncTaskParams* params = new AsyncTaskParams();
    params->client_instance = this;
    params->full_url = build_full_url(path, {}); // No query params in POST path typically
    params->method = HTTP_METHOD_POST;
    params->body = body;
    params->content_type = content_type;
    params->custom_headers = custom_headers;
    params->original_callback = callback;

    xTaskCreate(http_async_task_function, "http_post_task", 8192, params, 5, NULL);
}

void HTTPClient::put(const std::string& path, const std::string& body, HttpCallback callback, const std::string& content_type, const std::map<std::string, std::string>& custom_headers) {
    AsyncTaskParams* params = new AsyncTaskParams();
    params->client_instance = this;
    params->full_url = build_full_url(path, {});
    params->method = HTTP_METHOD_PUT;
    params->body = body;
    params->content_type = content_type;
    params->custom_headers = custom_headers;
    params->original_callback = callback;

    xTaskCreate(http_async_task_function, "http_put_task", 8192, params, 5, NULL);
}

void HTTPClient::del(const std::string& path, HttpCallback callback, const std::map<std::string, std::string>& custom_headers) {
    AsyncTaskParams* params = new AsyncTaskParams();
    params->client_instance = this;
    params->full_url = build_full_url(path, {});
    params->method = HTTP_METHOD_DELETE;
    params->custom_headers = custom_headers;
    params->original_callback = callback;
    
    xTaskCreate(http_async_task_function, "http_del_task", 8192, params, 5, NULL);
}

void HTTPClient::patch(const std::string& path, const std::string& body, HttpCallback callback, const std::string& content_type, const std::map<std::string, std::string>& custom_headers) {
    AsyncTaskParams* params = new AsyncTaskParams();
    params->client_instance = this;
    params->full_url = build_full_url(path, {});
    params->method = HTTP_METHOD_PATCH;
    params->body = body;
    params->content_type = content_type;
    params->custom_headers = custom_headers;
    params->original_callback = callback;

    xTaskCreate(http_async_task_function, "http_patch_task", 8192, params, 5, NULL);
}
